///	@NOTE THIS IS AN TEST APPLICATION. To compile it, remove any
/// UNIT TEST include from CoolVES_initmain.cpp. Then ensure
/// that this testapp is linked to CoolVES_initmain.cpp


// This test shows directly how fast we can update the parent-childern tree.
#include <stdint.h>
#include <iostream>
#include <sstream>
#include <cmath>
#include <list>
#include <ctime>
#include <cstdlib>

#include <Logger/CoutLogger.h>
#include <SubSystemBase/SubSystemManager.h>
#include <ObjectSubSystem/ObjectSubSystem.h>
#include <RenderSubSystem/RenderSubSystem.h>
#include <PhysicsSubSystem/PhysicsSubSystem.h>
#include <Exceptions/Exception.h>
#include <Math/Vector.hpp>
#include <Math/Matrix4x4.hpp>


const size_t MAX_OBJECTS = 100000;
const size_t MAX_FPATENTEDOBJECTS = 50;

float RandFloat(float lower, float higher)
{
	return (float(rand()) / (float)RAND_MAX) * fabs(higher+lower) + lower;
}

float RandFloat()
{
	return (float(rand()) / (float)RAND_MAX);
}

float RandInt(int lower, int higher)
{
	return (float(rand()) / (float)RAND_MAX) * higher + lower;
}

int RandInt(int range)
{
	return (int)floor((float(rand()) / RAND_MAX) * range + 0.5f);
}

typedef cvRenderInterface::BatchObject<cvRenderInterface::Line> LineBatch_t;

void generate_tree(EmptyObject** &objtable, EmptyObject* &ROOT, LineBatch_t* &batch)
{
	EmptyObject *blob = 0, *placer = 0;
	size_t i = 0, j = 0;
	int gencount = 0;
	std::list<EmptyObject*> bloblist;

	LOGINFO("Allocating objects..");

	objtable = new EmptyObject* [MAX_OBJECTS];
	memset(objtable, 0, sizeof(EmptyObject*) * MAX_OBJECTS);

	for(i = 0; i < MAX_OBJECTS; ++i)
	{
		objtable[i] = ObjectSubSystem::getInstance()->create<EmptyObject>();
	}

	LOGINFO("Creating The Tree Of Death...");
	i = 0;
	srand(time(0));
	while(i < MAX_OBJECTS)
	{
		j = i++;
		bloblist.push_back(objtable[j]);
		objtable[j]->turn(RandFloat()*10.0f - RandFloat()*10.0f);
		gencount = RandInt(MAX_FPATENTEDOBJECTS);
		while((i < MAX_OBJECTS) && (gencount > 0))
		{
			objtable[i]->setParent(objtable[j]);
			objtable[i]->turn(RandFloat()*30.0f - RandFloat()*30.0f);
			objtable[i]->move(RandFloat()*15.0f);
			--gencount;
			++i;
		}
	}

	// parent blobs to ROOT
	ROOT = bloblist.front();
	bloblist.pop_front();
	ROOT->rotate(90.0f);

	while(!bloblist.empty())
	{
		blob = bloblist.front();
		bloblist.pop_front();
		placer = ROOT;

		// iterate into random place in the tree.
		while(placer->countChildren() > 0)
		{
			placer = (EmptyObject*)placer->getChild(RandInt(placer->countChildren()-1));
		}
		// parent the blob with it.
		blob->setParent(placer);
		blob->move(RandFloat()*30.0f + 10.0f);
		blob->turn(RandFloat(-1.0f, 1.0)*10.0f);
	}


	RenderingDevice::getInstance()->OpenScreen(800, 600);

	LOGINFO("Allocating renderer resources for the tree...");
	batch = RenderingDevice::getInstance()->getRenderer()->lineFactory(true);
	cvRenderInterface::Line * linetmp = 0;
	for(i = 0; i < MAX_OBJECTS; ++i)
	{
		if(objtable[i]->getParent())
		{
			batch->map(&linetmp, 1);
			objtable[i]->setData(linetmp);
		}
	}
}

void coolves_TheTreeOfDeath()
{
	using namespace cvMath;

	/// Alpha Cool VES bootstrap!

	RenderingDevice * rensys = RenderingDevice::getInstance();
	ObjectSubSystem * objsys = ObjectSubSystem::getInstance();
	LineBatch_t * linebatch = 0;
	cvRenderInterface::Line * linetmp;
	EmptyObject **objtable = 0, *ROOT = 0, *placer = 0, *itrobj;

	generate_tree(objtable, ROOT, linebatch);
	placer = ROOT;

	glfwSetWindowTitle("Cool VES cpp2: The Tree of DEATH - 15000 objects share single object as root.");
	LOGINFO("NGGGHHHH!!!! test RUN!");

	Mat4x4f camera_modelview, camera_projmat, tempmat;
	Vec2f camera_pos(0.0, 0.0);
	Vec2f camera_offset(0.0, 0.0);
	float camera_angle = 0.0f;
	float camera_zoom = 0.8f;

	const RGBAf GREEN(0.0f, 0.7f, 0.0f, 0.4f);
	const RGBAf BROWN(0.329411764f, 0.203921568f, 0.137254901f, 0.9f);
	const RGBAf DOCBROWN(0.229411764f, 0.103921568f, 0.037254901f, 1.0f);


	bool running = true;
	while(running)
	{
		int w, h, x, y;
		glfwGetWindowSize(&w, &h);
		glfwGetMousePos(&x, &y);

		camera_zoom += (glfwGetKey(GLFW_KEY_KP_ADD) - glfwGetKey(GLFW_KEY_KP_SUBTRACT)) * 0.01f;
		camera_offset.x = (x - w/2) / (float)w;
		camera_offset.y = -(y - h/2) / (float)h;
		camera_angle += (glfwGetKey(GLFW_KEY_KP_4) - glfwGetKey(GLFW_KEY_KP_6));
		camera_pos.x += ((glfwGetKey(GLFW_KEY_KP_3) - glfwGetKey(GLFW_KEY_KP_1))) / camera_zoom;
		camera_pos.y += ((glfwGetKey(GLFW_KEY_KP_2) - glfwGetKey(GLFW_KEY_KP_5))) / camera_zoom;

		// "WORLD COORDINATE MODE"
		//renderer->setCamera(0);
		//{
		// SETUP VIEWPORT
		glViewport(0, 0, w, h);

		// SETUP PROJECTION MATRIX
		identity(camera_projmat);	// set identity
		camera_projmat *= ortho_world2d((float)w, (float)h);	// apply screen coordinate projection

		// SETUP MODELVIEW MATRIX - todo, too mucho 3d effect? A BIT?
		identity(camera_modelview);											// set identity
		translationXY(camera_modelview, camera_offset * -Vec2f(w, h));		// insert transation
		camera_modelview *= scaleXY(camera_zoom, camera_zoom);				// multiply by scaling matrix.
		// multiply by translation-rotation matrix.
		camera_modelview *= rotationZ(translationXY(identity(tempmat), camera_pos+(camera_offset * Vec2f(w, h))), camera_angle);

		// LOAD MATRIXES
		glMatrixMode(GL_PROJECTION);
		glLoadMatrixf((GLfloat*)&camera_projmat.mat);
		glMatrixMode(GL_MODELVIEW);
		glLoadMatrixf((GLfloat*)&camera_modelview.mat);

		// MASSIVE. IT IS MASSIVE.

		// rotate whole thing..
		ROOT->turn(0.001f);
		// plus turn random objects to morph the three
		for(size_t i = 0, j = RandInt(MAX_OBJECTS/8); i < j; ++i)
		{
			objtable[RandInt(MAX_OBJECTS-2)+1]->turn(RandFloat()*5.0f - RandFloat()*5.0f);
			objtable[RandInt(MAX_OBJECTS-2)+1]->move(RandFloat() * 10.0f);
		}

		ROOT->update();
		objsys->updateObjects();



		// UPDATE VERTICLES.
		for(size_t i = 0; i < MAX_OBJECTS; ++i)
		{
			itrobj = objtable[i];
			linetmp = (cvRenderInterface::Line*)itrobj->getData();
			if(linetmp)
			{
				linetmp->verts(itrobj->position(), ((EmptyObject*)itrobj->getParent())->position());

				if(itrobj->countChildren() > 0)
				{
					linetmp->color(BROWN);
				}
				else if(itrobj->countChildren() == 0)
				{
					linetmp->color(GREEN);
				}

				if(itrobj->getParent() == placer)
				{
					linetmp->color(DOCBROWN);
				}


			}
		}

		rensys->getRenderer()->render();
		rensys->flip();

		if(glGetError() != GL_NO_ERROR)
		{
			throw new Exception("got GL ERRORS!");
		}
		if(glfwGetKey(GLFW_KEY_ESC) || glfwGetWindowParam(GLFW_OPENED) == GL_FALSE)	{ running = false; }

	}	// while(running)...


	LOGINFO("Cleanup...");
	for(size_t i = 0; i < MAX_OBJECTS; ++i)
	{
		objsys->deleteObject(objtable[i]);
	}
	delete[] objtable;
}
